#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <queue>


/*------------ 代码整体框架逻辑 -----------
主体框架
队列满时，生产者wait等待
队列为空是，消费者wait等待

-------生产者----------
生产一个数据，那么队列一定是有数据的 -> 通知消费者。
生产满在消费还是生产一个在消费由我们代码决定
如果不通知消费者，那么消费者拿锁但是不满足条件变量那么在wait等待，等待的时候会把锁释放调，供生产者使用。
当生产者生产满的时候，会在wait等待

-------消费者----------
消费一个数据，那么队列一定是由空位的 -> 通知生产者生产


----------------------------------------*/


const int defaultcap = 5;

// 阻塞队列
namespace pc
{
    template <class T>
    class BlockQueue
    {
    protected:
        // 队列为满
        bool IsFull()
        {
            return (_q.size() >= _size);
        }

        // 队列为空
        bool IsEmpty(void)
        {
            return _q.empty();
        }

    public:
        BlockQueue() : _size(defaultcap)
        {
            pthread_mutex_init(&mutex, nullptr);

            pthread_cond_init(&pcond, nullptr);
            pthread_cond_init(&ccond, nullptr);
        }

        //生产者
        void production(const T& in)
        {
            
            pthread_mutex_lock(&mutex);
            //队列满
            while(IsFull())
            {
                std::cout << "生产者进入休眠" << std::endl;
                pthread_cond_wait(&pcond,&mutex);

            }
            //队列未满
            _q.push(in);
            pthread_cond_signal(&ccond);

            pthread_mutex_unlock(&mutex);
            
        }

        //消费者
        T consumer()
        {
            //队列为空
            pthread_mutex_lock(&mutex);
            while(IsEmpty())
            {   

                std::cout << "消费者进入休眠" << std::endl;
                pthread_cond_wait(&ccond,&mutex);
            }

            //队列不为空
            T t = _q.front();
            _q.pop();
            pthread_cond_signal(&pcond);


            pthread_mutex_unlock(&mutex);

            return t;


        }


        ~BlockQueue()
        {
            pthread_mutex_destroy(&mutex);

            pthread_cond_destroy(&pcond);
            pthread_cond_destroy(&ccond);
        }

    private:
        int _size;   // 队列大小
        std::queue<T> _q; // 队列

        //这里只能够用一个锁，如果生产消费分别用两个锁，对用一个队列进程操作，那么队列的数据就会出错
        //典型问题：上产过程中被消费了
        pthread_mutex_t mutex; 

        pthread_cond_t pcond; // 生产者的条件变量
        pthread_cond_t ccond;     // 消费者的条件变量

        pthread_t p, c;
    };
}
